package com.chuangke.sinotrukbudget.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.chuangke.admin.model.SysDept;
import com.chuangke.admin.service.SysDataPermissionService;
import com.chuangke.admin.service.SysDeptService;
import com.chuangke.common.constant.Consts;
import com.chuangke.common.constant.Masks;
import com.chuangke.common.enums.OrderStatusEnum;
import com.chuangke.common.utils.SecurityUtils;
import com.chuangke.core.exception.ChuangkeException;
import com.chuangke.core.id.IdManager;
import com.chuangke.flow.constant.FlowConstant;
import com.chuangke.flow.domain.FlowAuditResult;
import com.chuangke.flow.domain.FlowFormModel;
import com.chuangke.flow.domain.FlowHandler;
import com.chuangke.flow.domain.OrderModel;
import com.chuangke.flow.service.FlowService;
import com.chuangke.framework.model.BasicModel;
import com.chuangke.framework.model.Record;
import com.chuangke.olap.OlapProcessor;
import com.chuangke.olap.context.OlapConfig;
import com.chuangke.olap.context.OlapPackage;
import com.chuangke.olap.dismantle.OlapDataDismantler;
import com.chuangke.olap.enums.FormulaEnum;
import com.chuangke.olap.model.OlapFactor;
import com.chuangke.olap.model.Property;
import com.chuangke.olap.processor.SingleOlapProcessor;
import com.chuangke.olap.view.OlapColumn;
import com.chuangke.olap.view.feview.FeViewBuilder;
import com.chuangke.sinotrukbudget.consts.BudgetContanst;
import com.chuangke.sinotrukbudget.manager.BudgetManager;
import com.chuangke.sinotrukbudget.model.BudgetAccount;
import com.chuangke.sinotrukbudget.model.BudgetBpcPlan;
import com.chuangke.sinotrukbudget.model.BudgetDeptAccount;
import com.chuangke.sinotrukbudget.sdao.BudgetBpcPlanDaoService;
import com.chuangke.sinotrukbudget.service.BudgetAccountService;
import com.chuangke.sinotrukbudget.service.BudgetAuditControlService;
import com.chuangke.sinotrukbudget.service.BudgetBpcPlanService;
import com.chuangke.sinotrukbudget.service.BudgetControlAccountDeptService;
import com.chuangke.sinotrukbudget.service.BudgetDeptAccountService;
import com.chuangke.sinotrukbudget.service.BudgetDingTalkTodoService;
import com.chuangke.sinotrukbudget.service.BudgetTodoService;
import com.chuangke.sinotrukbudget.util.BudgetUtil;
import com.chuangke.sinotrukbudget.util.BudgetValidateUtil;
import com.chuangke.system.annotation.ApiLogDoc;

/**
 * bpc预算上报 Service业务层处理
 *
 * @author chuangke
 * @date "2022-10-21"
 */
@Service
public class BudgetBpcPlanServiceImpl implements BudgetBpcPlanService {

	private static Logger logger = LoggerFactory.getLogger(BudgetBpcPlanServiceImpl.class);

	public static final String CACHE_NAME = "budget_bpc_plan";

	@Autowired
	private BudgetAccountService budgetAccountService;
	@Autowired
	private SysDeptService sysDeptService;
	@Autowired
	private BudgetBpcPlanDaoService budgetBpcPlanDaoService;
	@Autowired
	private FlowService flowService;
	@Autowired
	private BudgetManager budgetManager;
	@Autowired
	public BudgetDeptAccountService budgetDeptAccountService;
	@Autowired
	public SysDataPermissionService sysDataPermissionService;

	@Autowired
	private BudgetDingTalkTodoService budgetDingTalkTodoService;
	@Autowired
	private BudgetTodoService budgetTodoService;

	@Autowired
	private BudgetAuditControlService budgetAuditControlService;
	@Autowired
	private BudgetControlAccountDeptService budgetControlAccountDeptService ;

	/****************************** 流程驱动服务 ********************************/

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void submit(String id, Map<String, Object> variables) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode())
						.eq(BudgetBpcPlan::getId, id).eq(BudgetBpcPlan::getStatus, OrderStatusEnum.EDIT.getCode()));
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void reject(String id, Map<String, Object> variables) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.EDIT.getCode())
						.eq(BudgetBpcPlan::getId, id).eq(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode()));
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void complete(String id, Map<String, Object> variables) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.COMPLETE.getCode())
						.eq(BudgetBpcPlan::getId, id).eq(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode()));
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void submitBatch(List<String> orderIdList) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode())
						.in(BudgetBpcPlan::getId, orderIdList)
						.eq(BudgetBpcPlan::getStatus, OrderStatusEnum.EDIT.getCode()));
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void cancelSubmitBatch(List<String> orderIdList) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.EDIT.getCode())
						.in(BudgetBpcPlan::getId, orderIdList)
						.eq(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode()));
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void rejectBatch(List<String> orderIdList) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.EDIT.getCode())
						.in(BudgetBpcPlan::getId, orderIdList)
						.eq(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode()));
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void completeBatch(List<String> orderIdList) {
		budgetBpcPlanDaoService.update(new BudgetBpcPlan(),
				Wrappers.<BudgetBpcPlan>lambdaUpdate().set(BudgetBpcPlan::getStatus, OrderStatusEnum.COMPLETE.getCode())
						.in(BudgetBpcPlan::getId, orderIdList)
						.eq(BudgetBpcPlan::getStatus, OrderStatusEnum.APPROVAL.getCode()));
	}

	/********************************** 预算流程操作服务 ********************************/

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@ApiLogDoc(apiId = "-", apiName = "流程启动并提交", reqType = "-")
	public int startFlowAndSubmit() {
		List<BudgetBpcPlan> bbpList = budgetBpcPlanDaoService.findUnStartFlowData();
		Map<String, List<BudgetBpcPlan>> bbpMap = bbpList.stream()
				.collect(Collectors.groupingBy(BudgetBpcPlan::getSubmitter));

		bbpMap.keySet().forEach(submiter -> startFlowAndSubmit(submiter, bbpMap.get(submiter)));
		return bbpList.size();
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@ApiLogDoc(apiId = "-", apiName = "流程启动并提交", reqType = "-")
	public void startFlowAndSubmit(String auditUser, List<BudgetBpcPlan> bbpList) {
		startProcessInstance(bbpList);
		autoSubmitBudget(auditUser, bbpList);
	}

	/**
	 * 启动流程实例
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void startProcessInstance(List<BudgetBpcPlan> bbpList) {
		List<OrderModel> list = new ArrayList<>();
		bbpList.forEach(bbp -> {
			FlowFormModel handler = new FlowFormModel();

			HashMap<String, Object> map = new HashMap<>();
			map.put("dept_type", bbp.getDeptType());
			map.put("t_account", bbp.gettAccount());

			handler.setOrderId(bbp.getId());
			handler.setOrderType(bbp.getBisType());
			handler.setOrderRemarks(bbp.getRemarks());
			handler.setOrderCreatedAt(new Date());
			handler.setOrderCreatedBy(bbp.getSubmitter());
			handler.setOrderService(BudgetBpcPlanService.class.getName());
			handler.setOrderDeptId(bbp.gettEntity());
			handler.setVariables(map);

			list.add(handler);
		});

		flowService.startProcessInstanceBatch(BudgetContanst.BUDGET_FLOW_ID, list);
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public FlowAuditResult submitBudget(String tCategory, String time, String deptId, List<String> accountIds) {
		BudgetValidateUtil.validateParam(tCategory, time, deptId, accountIds);

		List<BudgetBpcPlan> list = budgetBpcPlanDaoService.findUnSubmitFlowData(tCategory, deptId, time, accountIds);

		return doCompletBudget(true, list, FlowConstant.VAR_AUDIT_OPT_AGREE, "");
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public FlowAuditResult autoSubmitBudget(String auditUser, List<BudgetBpcPlan> list) {
		return doCompletBudget(true, list, auditUser, FlowConstant.VAR_AUDIT_OPT_AGREE, "");
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public FlowAuditResult auditBudget(String audit, String message, String calType, String tCategory, String time,
			List<String> deptIds, List<String> accountIds) {
		BudgetValidateUtil.validateParam(tCategory, time);

		String status = FlowConstant.OPT_STATE_MAP.get(audit);
		if (StrUtil.isBlank(status)) {
			throw new ChuangkeException("审批参数不对,未传输审批标识");
		}

		List<BudgetBpcPlan> bbpList = budgetBpcPlanDaoService.findAuditedOrUnAuditedData(
				BudgetContanst.AUDIT_STATUS_DSH, tCategory, time, deptIds, accountIds, null);

		return doCompletBudget(false, bbpList, audit, message);
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public FlowAuditResult revokeBudget(String audit, String message, String calType, String tCategory, String time,
			List<String> deptIds, List<String> accountIds) {
		BudgetValidateUtil.validateParam(tCategory, time);

		String status = FlowConstant.OPT_STATE_MAP.get(audit);
		if (StrUtil.isBlank(status)) {
			throw new ChuangkeException("审批参数不对,未传输审批标识");
		}

		List<BudgetBpcPlan> list = budgetBpcPlanDaoService.findAuditedOrUnAuditedData(BudgetContanst.AUDIT_STATUS_YSH,
				tCategory, time, deptIds, accountIds, null);
		if (CollectionUtils.isEmpty(list)) {
			throw new ChuangkeException("未找到可取回的数据");
		}
		return doCompletBudget(false, list, FlowConstant.VAR_AUDIT_OPT_REVOKE, message);
	}

	private FlowAuditResult doCompletBudget(boolean isSubmitRestrictTaskUser, List<BudgetBpcPlan> bbpList, String audit,
			String message) {
		String userId = SecurityUtils.getUserId();
		return doCompletBudget(isSubmitRestrictTaskUser, bbpList, userId, audit, message);
	}

	private FlowAuditResult doCompletBudget(boolean isSubmitRestrictTaskUser, List<BudgetBpcPlan> bbpList,
			String auditUser, String audit, String message) {
		if(CollectionUtils.isEmpty(bbpList)) {
			throw new ChuangkeException("请选择明细预算科目进行处理") ;
		}
		List<FlowHandler> bbpIdList = bbpList.stream().map(bbp -> {
			FlowHandler handler = new FlowHandler();
			handler.setAudit(audit);
			handler.setBusinessKey(bbp.getId());
			handler.setUserId(auditUser);
			handler.setMessage(message);
			handler.setSubmitRestrictTaskUser(isSubmitRestrictTaskUser);
			return handler;
		}).collect(Collectors.toList());

		List<String> orderIdList = bbpList.stream().map(BudgetBpcPlan::getId).collect(Collectors.toList());
		List<String> todoUserListBefore = budgetTodoService.getTodoUser(orderIdList);

		FlowAuditResult flowAuditResult = null;
		if (FlowConstant.VAR_AUDIT_OPT_REVOKE.equals(audit)) {
			flowAuditResult = flowService.revokeBatch(bbpIdList);
		} else {
			flowAuditResult = flowService.completeBatch(bbpIdList);
		}

		FlowAuditResult finalFlowAuditResult = BudgetUtil.handleFlowAuditResult(flowAuditResult, bbpList);

		List<String> todoUserListAfter = budgetTodoService.getTodoUser(orderIdList);

		List<String> handleTodoUser = new ArrayList<>();
		handleTodoUser.addAll(todoUserListBefore);
		handleTodoUser.addAll(todoUserListAfter);

		/* 异步创建钉钉待办任务，删除钉钉待办任务 */
		budgetDingTalkTodoService.handleDingTalkTodo(handleTodoUser);

		return finalFlowAuditResult;
	}

	/******************************** 预算保存服务 ********************************/

	@Override
	@Transactional(rollbackFor = Throwable.class)
	@CacheEvict(cacheNames = CACHE_NAME, key = "#deptId+'-'+#category+'-'+#time")
	public void save(String category, String deptId, String time, List<Map<String, Object>> bbpList) {
		/* 概算保存和其他保存有差异，概算有业务预算，保存需要扣减业务预算 */
		if (category.startsWith("BUD")) {
			saveBUD(category, deptId, time, bbpList);
		} else {
			saveBudget(category, deptId, time, bbpList);
		}
		handleHandleTime() ;
	}

	/**
	 * 概算预算保存
	 *
	 * @param category
	 * @param deptId
	 * @param time
	 * @param bbpList
	 */
	public void saveBUD(String category, String deptId, String time, List<Map<String, Object>> bbpList) {
		if (CollectionUtils.isEmpty(bbpList)) {
			return;
		}

		bbpList = bbpList.stream().filter(bbp -> OrderStatusEnum.EDIT.getCode().equals(bbp.get("status")))
				.collect(Collectors.toList());

		if (CollectionUtils.isEmpty(bbpList)) {
			throw new ChuangkeException("数据为非编制状态，不能修改");
		}

		String[] rowDimensions = new String[] { "t_account" };
		String[] colDimensions = new String[] { "t_time", "t_flow" };
		OlapFactor[] calMeasures = new OlapFactor[] { new OlapFactor("signdata", FormulaEnum.SUM) };

		Map<String, Object> initDataMap = new HashMap<>();
		initDataMap.put("deptId", deptId);
		initDataMap.put("category", category);

		List<BudgetBpcPlan> bbpDataList = OlapDataDismantler.convert(rowDimensions, colDimensions, calMeasures, bbpList,
				initDataMap, getBeanAndColumnMap(), BudgetBpcPlan.class);
		bbpDataList.forEach(bbp -> bbp.settAccount(bbp.gettAccount().replaceAll(Consts.BLANK_CHAR + "", "").trim()));

		// 排除合计
		bbpDataList = bbpDataList.stream().filter(bbp -> !"sum".equals(bbp.gettTime()) && !"sum".equals(bbp.gettFlow()))
				.collect(Collectors.toList());

		// 获取需要保存的数据
		Map<String, BudgetBpcPlan> needSaveDataMap = bbpDataList.stream().collect(Collectors.toMap(a -> {
			a.settAudit("INPUT_E3");
			return BudgetUtil.getStoreUniqeKey(a);
		}, a -> a));

		// 获取业务预算数据
		List<BudgetBpcPlan> ywysDataList = budgetBpcPlanDaoService.findYwysBudget(category, deptId, time);
		Map<String, BudgetBpcPlan> ywysDataMap = ywysDataList.stream().collect(Collectors.toMap(a -> {
			a.settAudit("INPUT_E3");
			return BudgetUtil.getStoreUniqeKey(a);
		}, a -> a));

		// ywysDataList的所有可以肯定都在needSaveDataMap中
		// needSaveDataMap中的预算数据包含了业务预算和科目预算，这里保存的是科目预算，所以需要将业务预算减去，才是真实需要保存的科目预算
		for (String key : needSaveDataMap.keySet()) {
			if (ywysDataMap.keySet().contains(key)) {
				needSaveDataMap.get(key).setSigndata(
						needSaveDataMap.get(key).getSigndata().subtract(ywysDataMap.get(key).getSigndata()));
			}
		}

		List<BudgetBpcPlan> kmysList = budgetBpcPlanDaoService.findKmysBudget(category, deptId, time);

		Map<String, BudgetBpcPlan> existsKmysDataMap = kmysList.stream()
				.collect(Collectors.toMap(BudgetUtil::getStoreUniqeKey, a -> a));

		// 只有编辑状态的科目才能修改
		List<BudgetBpcPlan> needUpdateKmysList = kmysList.stream()
				.filter(bbp -> OrderStatusEnum.EDIT.getCode().equals(bbp.getStatus())).collect(Collectors.toList());

		if (CollectionUtils.isEmpty(needUpdateKmysList)) {
			throw new ChuangkeException("数据为非编制状态，不能修改");
		}

		Map<String, BudgetBpcPlan> needUpdateKmysDataMap = needUpdateKmysList.stream()
				.collect(Collectors.toMap(BudgetUtil::getStoreUniqeKey, a -> a));

		// 只有这些科目可以修改
		List<String> canEditAccountList = budgetAccountService.findLastLevel().stream().map(BudgetAccount::getId)
				.collect(Collectors.toList());

		for (String key : needUpdateKmysDataMap.keySet()) {
			BudgetBpcPlan savebbp = needSaveDataMap.get(key);
			if (savebbp != null) {
				needUpdateKmysDataMap.get(key).setSigndata(savebbp.getSigndata());
			}
		}

		// 初始化需要插入的科目预算
		List<BudgetBpcPlan> needInsertKmysList = new ArrayList<>();
		for (String key : needSaveDataMap.keySet()) {
			// oldKmysDataMap存在，可以进行update,不需要insert
			if (existsKmysDataMap.keySet().contains(key)) {
				continue;
			}
			// 如果当前科目不能编辑，不需要insert
			if (!canEditAccountList.contains(needSaveDataMap.get(key).gettAccount())) {
				continue;
			}
			if (needSaveDataMap.get(key).getSigndata().compareTo(BigDecimal.ZERO) == 0) {
				continue;
			}
			needInsertKmysList.add(needSaveDataMap.get(key));
		}
		// 随便拿一个数据做模板
		BudgetBpcPlan templateBbp = needUpdateKmysList.get(0);
		needInsertKmysList.forEach(bbp -> {
			bbp.setId(IdManager.nextId());
			bbp.setBisType(templateBbp.getBisType());
			bbp.setInStatus(templateBbp.getInStatus());
			bbp.setInUuid(templateBbp.getInUuid());
			bbp.setDeptType(templateBbp.getDeptType());
			bbp.setOutStatus(templateBbp.getOutStatus());
			bbp.setOutUuid(templateBbp.getOutUuid());
			bbp.setStatus(templateBbp.getStatus());
			bbp.setSubmitter(templateBbp.getSubmitter());
			bbp.settFlow1(templateBbp.gettFlow1());
			bbp.settFlow2(templateBbp.gettFlow2());
			bbp.settFlow3(templateBbp.gettFlow3());
			bbp.settFlow4(templateBbp.gettFlow4());
			bbp.settFlow5(templateBbp.gettFlow5());
			bbp.settFlow6(templateBbp.gettFlow6());
			bbp.settObject(templateBbp.gettObject());
			bbp.setAccountType(BudgetAccount.ACCOUNT_TYPE_CW);
		});

		budgetBpcPlanDaoService.updateBatchById(needUpdateKmysList);

		// 新增的数据，并启动流程
		budgetBpcPlanDaoService.saveBatch(needInsertKmysList);
		startProcessInstance(needInsertKmysList);
	}

	/**
	 * 非概算预算保存
	 *
	 * @param category
	 * @param deptId
	 * @param time
	 * @param bbpList
	 */
	public void saveBudget(String category, String deptId, String time, List<Map<String, Object>> bbpList) {
		if (CollectionUtils.isEmpty(bbpList)) {
			return;
		}

		bbpList = bbpList.stream().filter(bbp -> OrderStatusEnum.EDIT.getCode().equals(bbp.get("status")))
				.collect(Collectors.toList());

		if (CollectionUtils.isEmpty(bbpList)) {
			throw new ChuangkeException("数据为非编制状态，不能修改");
		}

		String[] rowDimensions = new String[] { "t_account" };
		String[] colDimensions = new String[] { "t_time", "t_flow" };
		OlapFactor[] calMeasures = new OlapFactor[] { new OlapFactor("signdata", FormulaEnum.SUM) };

		Map<String, Object> initDataMap = new HashMap<>();
		initDataMap.put("deptId", deptId);
		initDataMap.put("category", category);

		List<BudgetBpcPlan> bbpDataList = OlapDataDismantler.convert(rowDimensions, colDimensions, calMeasures, bbpList,
				initDataMap, getBeanAndColumnMap(), BudgetBpcPlan.class);
		bbpDataList.forEach(bbp -> bbp.settAccount(bbp.gettAccount().replaceAll(Consts.BLANK_CHAR + "", "").trim()));

		// 排除合计
		bbpDataList = bbpDataList.stream().filter(bbp -> !"sum".equals(bbp.gettTime()) && !"sum".equals(bbp.gettFlow()))
				.collect(Collectors.toList());

		// 获取需要保存的数据
		Map<String, BudgetBpcPlan> needSaveDataMap = bbpDataList.stream().collect(Collectors.toMap(a -> {
			a.settAudit("INPUT");
			return BudgetUtil.getStoreUniqeKey(a);
		}, a -> a));

		List<BudgetBpcPlan> ysList = budgetBpcPlanDaoService.findBudget(category, deptId, time);
		Map<String, BudgetBpcPlan> existsYsDataMap = ysList.stream()
				.collect(Collectors.toMap(BudgetUtil::getStoreUniqeKey, a -> a));

		// 只有编辑状态的科目才能修改
		List<BudgetBpcPlan> needUpdateYsList = ysList.stream()
				.filter(bbp -> OrderStatusEnum.EDIT.getCode().equals(bbp.getStatus())).collect(Collectors.toList());

		if (CollectionUtils.isEmpty(needUpdateYsList)) {
			throw new ChuangkeException("数据为非编制状态，不能修改");
		}

		Map<String, BudgetBpcPlan> needUpdateYsDataMap = needUpdateYsList.stream()
				.collect(Collectors.toMap(BudgetUtil::getStoreUniqeKey, a -> a));

		// 只有这些科目可以修改
		List<String> canEditAccountList = budgetAccountService.findLastLevel().stream().map(BudgetAccount::getId)
				.collect(Collectors.toList());

		for (String key : needUpdateYsDataMap.keySet()) {
			BudgetBpcPlan savebbp = needSaveDataMap.get(key);
			if (savebbp != null) {
				needUpdateYsDataMap.get(key).setSigndata(savebbp.getSigndata());
			}
		}

		// 初始化需要插入的科目预算
		List<BudgetBpcPlan> needInsertYsList = new ArrayList<>();
		for (String key : needSaveDataMap.keySet()) {
			// oldKmysDataMap存在，可以进行update,不需要insert
			if (existsYsDataMap.keySet().contains(key)) {
				continue;
			}
			// 如果当前科目不能编辑，不需要insert
			if (!canEditAccountList.contains(needSaveDataMap.get(key).gettAccount())) {
				continue;
			}
			if (needSaveDataMap.get(key).getSigndata().compareTo(BigDecimal.ZERO) == 0) {
				continue;
			}
			needInsertYsList.add(needSaveDataMap.get(key));
		}
		// 随便拿一个数据做模板
		BudgetBpcPlan templateBbp = needUpdateYsList.get(0);
		needInsertYsList.forEach(bbp -> {
			bbp.setId(IdManager.nextId());
			bbp.setBisType(templateBbp.getBisType());
			bbp.setInStatus(templateBbp.getInStatus());
			bbp.setInUuid(templateBbp.getInUuid());
			bbp.setDeptType(templateBbp.getDeptType());
			bbp.setOutStatus(templateBbp.getOutStatus());
			bbp.setOutUuid(templateBbp.getOutUuid());
			bbp.setStatus(templateBbp.getStatus());
			bbp.setSubmitter(templateBbp.getSubmitter());
			bbp.settFlow1(templateBbp.gettFlow1());
			bbp.settFlow2(templateBbp.gettFlow2());
			bbp.settFlow3(templateBbp.gettFlow3());
			bbp.settFlow4(templateBbp.gettFlow4());
			bbp.settFlow5(templateBbp.gettFlow5());
			bbp.settFlow6(templateBbp.gettFlow6());
			bbp.settObject(templateBbp.gettObject());
			bbp.setAccountType(BudgetAccount.ACCOUNT_TYPE_CW);
		});

		budgetBpcPlanDaoService.updateBatchById(needUpdateYsList);

		// 新增的数据，并启动流程
		budgetBpcPlanDaoService.saveBatch(needInsertYsList);
		startProcessInstance(needInsertYsList);
	}

	/******************************* 预算查询操作服务 ********************************/

	/**
	 * calType:t_account:预算科目； t_entity：组织机构
	 */
	@Override
	public Map<String, Object> listAuditBudget(boolean isControl, String auditStatus, String calType, String tCategory,
			String time, List<String> deptIds, List<String> accountIds, List<String> flowIds) {
//		/* 2023.1.8 增加管控限制 */
//		if (isControl) {
////			deptIds = getControlObject(deptIds,BudgetAuditControl.CONTROL_TYPE_DEPT) ;
//			accountIds = getControlObject(accountIds, BudgetAuditControl.CONTROL_TYPE_ACCOUNT);
//		}

		OlapConfig config = new OlapConfig();
		config.setShowRowDimensionKey(true);
		config.setRowDimensionFixed(true);
		config.setLevel(true);
		config.setLevelStyle(OlapConfig.LEVEL_STYLE_INDENT);
		config.setColDimensionShowNull(true);

		List<OlapFactor> rowDimensionsList = Arrays.asList(new OlapFactor(calType));

		return getAuditOrUnAuditList(isControl,auditStatus, rowDimensionsList, tCategory, time, deptIds, accountIds, flowIds,
				config);
	}

	/**
	 * 获取管控对象
	 *
	 * @param original
	 * @param controlType
	 * @return
	 */
	private List<String> getControlObject(List<String> original, String controlType) {
		String userId = SecurityUtils.getUserId();

		List<String> controlObjectList = budgetAuditControlService.getBudgetAuditControlByUser(userId, controlType);

		if (CollectionUtils.isEmpty(controlObjectList)) {
			return original;
		}

		if (CollectionUtils.isEmpty(original)) {
			return controlObjectList;
		}

		controlObjectList.retainAll(original);

		return controlObjectList;
	}

	@Override
	public Map<String, Object> listAuditDetailBudget(String auditStatus, String calType, String tCategory, String time,
			List<String> deptIds, List<String> accountIds, List<String> flowIds) {
		if (BudgetContanst.CAL_TYPE_DEPT.equals(calType)) {
			if (CollectionUtils.isEmpty(deptIds) && deptIds.size() != 1) {
				throw new ChuangkeException("参数异常，只能传入一个单位编号");
			}
			List<SysDept> deptList = sysDeptService.findLastLevel(deptIds.get(0));
			deptIds = deptList.stream().map(BasicModel::getId).collect(Collectors.toList());
		}
		if (BudgetContanst.CAL_TYPE_ACCOUNT.equals(calType)) {
			if (CollectionUtils.isEmpty(accountIds) && accountIds.size() != 1) {
				throw new ChuangkeException("参数异常，只能传入一个科目编号");
			}
			List<BudgetAccount> accountList = budgetAccountService.findLastLevel(accountIds.get(0));
			accountIds = accountList.stream().map(BasicModel::getId).collect(Collectors.toList());
		}
		OlapConfig config = new OlapConfig();
		config.setShowRowDimensionKey(true);
		config.setRowDimensionFixed(false);

		List<OlapFactor> rowDimensionsList = Arrays.asList(new OlapFactor("t_entity"), new OlapFactor("t_account"),
				new OlapFactor("submitter", FormulaEnum.DISTINCT));

		return getAuditOrUnAuditList(false,auditStatus, rowDimensionsList, tCategory, time, deptIds, accountIds, flowIds,
				config);
	}

	private Map<String, Object> getAuditOrUnAuditList(boolean isControl,String auditStatus, List<OlapFactor> rowDimensionsList,
			String tCategory, String time, List<String> deptIds, List<String> accountIds, List<String> flowIds,
			OlapConfig config) {
		List<BudgetBpcPlan> list = budgetBpcPlanDaoService.findAuditedOrUnAuditedData(auditStatus, tCategory, time,
				deptIds, accountIds, flowIds);
		
		Map<String,List<String>> controlMap = budgetControlAccountDeptService.getBudgetControlByUser(SecurityUtils.getUserId()) ;
		
		if(isControl) {
			list = list.stream().filter(bbp -> {
				List<String> controlDeptList = controlMap.get(bbp.gettAccount()) ;
				return CollectionUtils.isEmpty(controlDeptList) ? true : controlDeptList.contains(bbp.gettEntity()) ;
			}).collect(Collectors.toList()) ;
		}

		Collection<Map<String, String>> todoCountList = getTodoCountList(list);

		Map<String, Long> deptGroup = todoCountList.stream()
				.collect(Collectors.groupingBy(bbp -> bbp.get("entity"), Collectors.counting()));
		Map<String, Long> accountGroup = todoCountList.stream()
				.collect(Collectors.groupingBy(bbp -> bbp.get("account"), Collectors.counting()));

		List<SysDept> deptList = sysDeptService.findKinshipTree(deptGroup.keySet());
		List<BudgetAccount> accountList = budgetAccountService.findKinshipTree(accountGroup.keySet());

		List<SysDept> newDeptList = sysDeptService.clone(deptList);
		List<BudgetAccount> newAccountList = budgetAccountService.clone(accountList);

		Map<String, Object> result = olapHandle(list, rowDimensionsList, config);
		result.put("depts", countByDept(newDeptList, deptGroup));
		result.put("accounts", countByAccount(newAccountList, accountGroup));
		return result;
	}

	private Collection<Map<String, String>> getTodoCountList(List<BudgetBpcPlan> list) {
		Map<String, Map<String, String>> uniqueList = new HashMap<>();
		list.forEach(bbp -> {
			String key = getCountUniqeKey(bbp);
			Map<String, String> data = new HashMap<>();
			data.put("category", bbp.gettCategory());
			data.put("entity", bbp.gettEntity());
			data.put("account", bbp.gettAccount());
//			data.put("time", bbp.gettTime());
			uniqueList.put(key, data);
		});
		return uniqueList.values();
	}

	private Map<String, Object> olapHandle(List<BudgetBpcPlan> list, List<OlapFactor> rowDimensionsList,
			OlapConfig config) {
		OlapPackage olapPackage = new OlapPackage();

//		List<OlapFactor> rowDimensionsList = new ArrayList<>();
//		for (String r : rowDimensions) {
//			rowDimensionsList.add(new OlapFactor(r));
//		}

		config.setShowSumColDimension(true);
		config.setColDimensionShowNull(true);
		config.setShowNullColDimensions(Arrays.asList("t_flow"));
		config.setSumColDimensionColumns(Arrays.asList("t_flow"));

		olapPackage.setRowDimensions(rowDimensionsList);
		olapPackage.setColDimensions(Arrays.asList(new OlapFactor("t_time"), new OlapFactor("t_flow")));
		olapPackage.setCalMeasures(Arrays.asList(new OlapFactor("signdata", FormulaEnum.SUM)));
		olapPackage.setHidenColumns(Arrays.asList(new OlapFactor("status", "status", FormulaEnum.DISTINCT)));
		olapPackage.setConfig(config);

		List<Record> dataSource = BudgetUtil.convert(list);
		List<Property> propertys = budgetManager.getProperty(new HashMap<>());

		OlapProcessor olapProcessor = new SingleOlapProcessor(dataSource, propertys, olapPackage);
		olapProcessor.build();
		List<OlapColumn> olapColumnList = olapProcessor.getColumns();

		FeViewBuilder feViewBuilder = FeViewBuilder.build(olapColumnList);

		Map<String, Object> result = new HashMap<>();
		result.put("columns", feViewBuilder.getColumns());
		result.put("datas", olapProcessor.getData());
		return result;
	}

	private List<SysDept> countByDept(List<SysDept> list, Map<String, Long> deptGroup) {
		Map<String, SysDept> deptMap = sysDeptService.findMap();
		list.forEach(dept -> {
			dept.setCount(new BigDecimal("0"));
			deptGroup.keySet().forEach(deptId -> {
				SysDept sd = deptMap.get(deptId);
				if (sd != null && sd.getDataDomain().startsWith(dept.getDataDomain())) {
					dept.setCount(dept.getCount().add(new BigDecimal(deptGroup.get(deptId))));
				}
			});
			if (!CollectionUtils.isEmpty(dept.getChildren())) {
				countByDept(dept.getChildren(), deptGroup);
			}
		});
		return list;
	}

	private List<BudgetAccount> countByAccount(List<BudgetAccount> list, Map<String, Long> accountGroup) {
		Map<String, BudgetAccount> accountMap = budgetAccountService.findMap();
		list.forEach(account -> {
			account.setCount(new BigDecimal("0"));
			accountGroup.keySet().forEach(deptId -> {
				BudgetAccount sd = accountMap.get(deptId);
				if (sd != null && sd.getDataDomain().startsWith(account.getDataDomain())) {
					BigDecimal currentCount = account.getCount() == null ? new BigDecimal("0") : account.getCount();
					account.setCount(currentCount.add(new BigDecimal(accountGroup.get(deptId))));
				}
			});
			if (!CollectionUtils.isEmpty(account.getChildren())) {
				countByAccount(account.getChildren(), accountGroup);
			}
		});
		return list;
	}

	@Override
	public Map<String, Object> listEditBudget(String status, String tCategory, String deptId, String time) {
		if (StrUtil.isBlank(deptId)) {
			return new HashMap<>();
		}
		if (StrUtil.isBlank(tCategory)) {
			return new HashMap<>();
		}

		List<String> accountIdList = budgetManager.getGuikoukm();
		if (CollectionUtils.isEmpty(accountIdList)) {
			return new HashMap<>();
		}

		List<BudgetBpcPlan> list = budgetBpcPlanDaoService.findEditBudgetData(status, tCategory, deptId, time,
				accountIdList);

		OlapConfig config = new OlapConfig();
		config.setShowRowDimensionKey(true);
		config.setRowDimensionFixed(true);
		config.setLevel(true);
		config.setLevelStyle(OlapConfig.LEVEL_STYLE_INDENT);

		List<OlapFactor> rowDimensionsList = Arrays.asList(new OlapFactor("t_account"));

		return olapHandle(list, rowDimensionsList, config);
	}

	@Override
	public Map<String, Object> listGuiKouBudget(String tCategory, String deptId, String guiKouId, String time) {
		List<BudgetDeptAccount> bdaList = budgetDeptAccountService.listByDepts(Arrays.asList(guiKouId));

		List<String> accountIdList = bdaList.stream().map(BudgetDeptAccount::getAccountId).collect(Collectors.toList());

		List<BudgetBpcPlan> list = budgetBpcPlanDaoService.findEditBudgetData(null, tCategory, deptId, time,
				accountIdList);

		OlapConfig config = new OlapConfig();
		config.setShowRowDimensionKey(true);
		config.setRowDimensionFixed(true);
		config.setLevel(true);
		config.setLevelStyle(OlapConfig.LEVEL_STYLE_INDENT);

		List<OlapFactor> rowDimensionsList = Arrays.asList(new OlapFactor("t_account"));

		return olapHandle(list, rowDimensionsList, config);
	}

	private Map<String, String> getBeanAndColumnMap() {
		Map<String, String> columnMapping = new HashMap<>();
		columnMapping.put("tEntity", "deptId");
		columnMapping.put("tCategory", "category");
		columnMapping.put("tAccount", "t_account");
		columnMapping.put("tTime", "t_time");
		columnMapping.put("tFlow", "t_flow");
		columnMapping.put("tFlow1", "t_flow1");
		columnMapping.put("tFlow2", "t_flow2");
		columnMapping.put("tFlow3", "t_flow3");
		columnMapping.put("tFlow4", "t_flow4");
		columnMapping.put("tFlow5", "t_flow5");
		columnMapping.put("tFlow6", "t_flow6");
		columnMapping.put("tObject", "t_object");
		columnMapping.put("signdata", "signdata");
		return columnMapping;
	}

	// 计数主键和存储主键的不同：填报预算的时候，一个科目数据库中存储了13条，流程中任务也是13个任务，但对于用户看到的待办任务是1个

	/**
	 * 计数主键：待办任务计数的唯一uinique索引
	 *
	 * @param bbp
	 * @return
	 */
	private String getCountUniqeKey(BudgetBpcPlan bbp) {
		return String.format("%s-%s-%s", bbp.gettEntity().replaceAll(Consts.BLANK_CHAR + "", "").trim(),
				bbp.gettCategory(), bbp.gettAccount().replaceAll(Consts.BLANK_CHAR + "", "").trim());
	}

	@Override
	public String getFlowId(String deptId, String category, String accountId, String time) {
		return budgetBpcPlanDaoService.findFlowId(deptId, category, accountId, time);
	}

	@Override
	public List<BudgetBpcPlan> findAuditedAndUnWriteBackBudget() {
		/* 确定可以返回的数据，公司和预算版本 */
		QueryWrapper<BudgetBpcPlan> queryWrapper = new QueryWrapper<>();
		queryWrapper.select("distinct t_entity,t_category,status");
		queryWrapper.lambda().eq(BudgetBpcPlan::getOutStatus, Masks.BOOL_NO).eq(BudgetBpcPlan::getAccountType,
				BudgetAccount.ACCOUNT_TYPE_CW);
		BudgetUtil.appendRestrictQueryWrapperAudit(queryWrapper);
		List<BudgetBpcPlan> bbpList = budgetBpcPlanDaoService.list(queryWrapper);

		Map<String, BudgetBpcPlan> bbpMap = new HashMap<>();
		List<String> canNotBackList = new ArrayList<>();
		for (BudgetBpcPlan bbp : bbpList) {
			String key = bbp.gettEntity() + bbp.gettCategory();
			if (!OrderStatusEnum.COMPLETE.getCode().equals(bbp.getStatus())) {
				canNotBackList.add(key);
			}
			if (OrderStatusEnum.COMPLETE.getCode().equals(bbp.getStatus())) {
				bbpMap.put(key, bbp);
			}
		}

		canNotBackList.forEach(bbpMap::remove);

		if (MapUtils.isEmpty(bbpMap)) {
			return new ArrayList<>();
		}

		/* 根据公司和预算版本获取实际可返回的数据 */
		QueryWrapper<BudgetBpcPlan> uw2 = new QueryWrapper<>();
		uw2.lambda().eq(BudgetBpcPlan::getOutStatus, Masks.BOOL_NO);
		uw2.lambda().and(wrapper -> bbpMap.values().forEach(bbp -> wrapper.or(wp -> wp
				.eq(BudgetBpcPlan::gettEntity, bbp.gettEntity()).eq(BudgetBpcPlan::gettCategory, bbp.gettCategory()))));

		return budgetBpcPlanDaoService.list(uw2);
	}

	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void receiveBudgetData(List<BudgetBpcPlan> bbpList) {
		if(CollectionUtils.isEmpty(bbpList)) {
			return ;
		}
		//删除业务预算
		deleteYwBudget(bbpList.get(0).gettEntity()) ;
		
		// 查一下是否已经推送过,只看财务预算，不能看业务预算，因为业务预算的科目可能被多个财务预算使用
		Map<String, BudgetAccount> accountMap = budgetAccountService.findMap() ;
		Map<String, BudgetBpcPlan> bbpMap = new HashMap<>();
		bbpList.stream().filter(bbp -> {
			BudgetAccount ba = accountMap.get(bbp.gettAccount()) ;
			return ba == null ? false : ba.getType().equals(BudgetAccount.ACCOUNT_TYPE_CW) ;
		}).forEach(bbp -> {
			String key = String.format("%s-%s-%s-%s", bbp.gettEntity(), bbp.gettCategory(), bbp.gettTime(),
					bbp.gettAccount());
			bbpMap.put(key, bbp);
		});

		QueryWrapper<BudgetBpcPlan> qw = new QueryWrapper<>();
		qw.select("distinct in_uuid");
		BudgetUtil.appendRestrictQueryWrapperAudit(qw) ;
		qw.lambda().and(wrapper -> bbpMap.values()
				.forEach(bbp -> wrapper.or(wp -> wp.eq(BudgetBpcPlan::gettEntity, bbp.gettEntity())
						.eq(BudgetBpcPlan::gettCategory, bbp.gettCategory()).eq(BudgetBpcPlan::gettTime, bbp.gettTime())
						.eq(BudgetBpcPlan::gettAccount, bbp.gettAccount())
						)));
		List<BudgetBpcPlan> existsList = budgetBpcPlanDaoService.list(qw);

		List<String> inUuidList = existsList.stream().map(BudgetBpcPlan::getInUuid).collect(Collectors.toList());

		if (!CollectionUtils.isEmpty(inUuidList)) {
			QueryWrapper<BudgetBpcPlan> qw1 = new QueryWrapper<>();
			qw1.lambda().in(BudgetBpcPlan::getInUuid, inUuidList);
			List<BudgetBpcPlan> bbpExitsList = budgetBpcPlanDaoService.list(qw1);
			List<String> orderIdList = bbpExitsList.stream().map(BudgetBpcPlan::getId).collect(Collectors.toList());

			flowService.deleteProcessInstance(orderIdList);
			budgetBpcPlanDaoService.remove(qw1);
		}

		List<BudgetAccount> accountList = budgetAccountService.findLastLevel();
		List<String> accountIdList = accountList.stream().map(BasicModel::getId).collect(Collectors.toList());

		// 系统过滤，只接收系统中配置的科目
		bbpList = bbpList.stream().filter(bbp -> accountIdList.contains(bbp.gettAccount()))
				.collect(Collectors.toList());

		budgetBpcPlanDaoService.saveBatch(bbpList);

		List<String> cacheEvictList = new ArrayList<>();
		bbpList.forEach(bbp -> {
			String cacheKey = String.format("%s-%s-%s", bbp.gettEntity(), bbp.gettCategory(), bbp.gettTime());
			if (!cacheEvictList.contains(cacheKey)) {
				((BudgetBpcPlanService) AopContext.currentProxy()).cacheEvict(bbp.gettEntity(), bbp.gettCategory(),
						bbp.gettTime());
				cacheEvictList.add(cacheKey);
			}
		});
	}
	
	/**
	 * 删除单位下的全部业务预算
	 * @param deptId
	 */
	private void deleteYwBudget(String deptId) {
		Map<String, BudgetAccount> accountMap = budgetAccountService.findMap() ;
		Map<String, List<BudgetAccount>> accountGroup = accountMap.values().stream().filter(ba -> !StrUtil.isBlank(ba.getType())).collect(Collectors.groupingBy(BudgetAccount::getType));
		
		List<String> cwList = accountGroup.get(BudgetAccount.ACCOUNT_TYPE_CW).stream().map(BudgetAccount::getId).collect(Collectors.toList()) ;
		List<String> ywList = accountGroup.get(BudgetAccount.ACCOUNT_TYPE_FZ).stream().map(BudgetAccount::getId).collect(Collectors.toList()) ;
		
		//判断业务预算的条件：（科目为业务预算） 或者 （科目为财务预算 并且 （F_FLOW1<>F1_NONE 或者 F_FLOW4<>F4_NONE））
		QueryWrapper<BudgetBpcPlan> qw = new QueryWrapper<>();
		qw.lambda().eq(BudgetBpcPlan::gettEntity, deptId) ;
		qw.lambda().and(qw1 -> {
			qw1.in(BudgetBpcPlan::gettAccount, ywList).or(wrapper ->{
				wrapper.in(BudgetBpcPlan::gettAccount, cwList).and(w ->{
					w.ne(BudgetBpcPlan::gettFlow1, "F1_NONE").or().ne(BudgetBpcPlan::gettFlow4, "F4_NONE") ;
				}) ;
			});
		}) ;
		
		budgetBpcPlanDaoService.remove(qw);
	}

	@Override
	public List<BudgetBpcPlan> findBudgetBpcPlanByUniqueIndex(Map<String, Object> params) {
//		QueryWrapper<BudgetBpcPlan> queryWrapper = new QueryWrapper<BudgetBpcPlan>();
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettEntity, params.get("T_ENTITY"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettCategory, params.get("T_CATEGORY"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettAudit, params.get("T_AUDIT"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettAccount, params.get("T_ACCOUNT"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettTime, params.get("T_TIME"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow, params.get("T_FLOW"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow1, params.get("T_FLOW1"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow2, params.get("T_FLOW2"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow3, params.get("T_FLOW3"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow4, params.get("T_FLOW4"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow5, params.get("T_FLOW5"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettFlow6, params.get("T_FLOW6"));
//		queryWrapper.lambda().eq(BudgetBpcPlan::gettObject, params.get("T_OBJECT"));
//
//		return budgetBpcPlanDaoService.list(queryWrapper);

		List<BudgetBpcPlan> result = new ArrayList<>();

		String deptId = (String) params.get("T_ENTITY");
		String category = (String) params.get("T_CATEGORY");
		String time = (String) params.get("T_TIME");

		String audit = (String) params.get("T_AUDIT");
		String account = params.get("T_ACCOUNT").toString();
		String flow = (String) params.get("T_FLOW");
		String flow1 = (String) params.get("T_FLOW1");
		String flow2 = (String) params.get("T_FLOW2");
		String flow3 = (String) params.get("T_FLOW3");
		String flow4 = (String) params.get("T_FLOW4");
		String flow5 = (String) params.get("T_FLOW5");
		String flow6 = (String) params.get("T_FLOW6");
		String object = (String) params.get("T_OBJECT");

		List<BudgetBpcPlan> allList = ((BudgetBpcPlanService) AopContext.currentProxy())
				.findBudgetBpcPlanWithCache(deptId, category, time);

		allList.forEach(bbp -> {
			if (bbp.gettAudit().equals(audit) && bbp.gettAccount().equals(account) && bbp.gettFlow().equals(flow)
					&& bbp.gettFlow1().equals(flow1) && bbp.gettFlow2().equals(flow2) && bbp.gettFlow3().equals(flow3)
					&& bbp.gettFlow4().equals(flow4) && bbp.gettFlow5().equals(flow5) && bbp.gettFlow6().equals(flow6)
					&& bbp.gettObject().equals(object)) {
				result.add(bbp);
			}
		});

		return result;
	}

	@Override
	@Cacheable(cacheNames = CACHE_NAME, key = "#deptId+'-'+#category+'-'+#time")
	public List<BudgetBpcPlan> findBudgetBpcPlanWithCache(String deptId, String category, String time) {
		QueryWrapper<BudgetBpcPlan> queryWrapper = new QueryWrapper<>();
		queryWrapper.lambda().eq(BudgetBpcPlan::gettEntity, deptId);
		queryWrapper.lambda().eq(BudgetBpcPlan::gettCategory, category);
		queryWrapper.lambda().eq(BudgetBpcPlan::gettTime, time);
		return budgetBpcPlanDaoService.list(queryWrapper);
	}

	@Override
	@CacheEvict(cacheNames = CACHE_NAME, key = "#deptId+'-'+#category+'-'+#time")
	public void cacheEvict(String deptId, String category, String time) {
		logger.info("{}-{}-{}：缓存失效", deptId, category, time);
	}

	/**
	 * 处理处理时间
	 */
	@Async
	@Override
	public void handleHandleTime() {
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		QueryWrapper<BudgetBpcPlan> qw = new QueryWrapper<>();
		qw.select("distinct t_category,t_entity,t_time");
		qw.lambda().ne(BudgetBpcPlan::getStatus, OrderStatusEnum.COMPLETE.getCode()).like(BudgetBpcPlan::gettCategory,
				"BUD");// 只有概算
		List<BudgetBpcPlan> list = budgetBpcPlanDaoService.list(qw);
		Map<String, List<BudgetBpcPlan>> groupMap = list.stream()
				.collect(Collectors.groupingBy(b -> b.gettCategory() + "|" + b.gettEntity()));

		for (List<BudgetBpcPlan> bl : groupMap.values()) {
			if (bl.size() <= 1) {
				continue;
			}
			String handleTime = getHandleTime(bl);
			budgetBpcPlanDaoService.update(Wrappers.<BudgetBpcPlan>lambdaUpdate()
					.set(BudgetBpcPlan::getHandleTime, handleTime).eq(BudgetBpcPlan::gettCategory, bl.get(0).gettCategory())
					.eq(BudgetBpcPlan::gettEntity, bl.get(0).gettEntity()));
		}
	}

	private String getHandleTime(List<BudgetBpcPlan> bl) {
		if (bl.size() > 2) {
			//概算中正常只有两个期间，一个当前年度，一个下一个年度
			String times = String.join(",", bl.stream().map(BudgetBpcPlan::gettTime).collect(Collectors.toList()));
			throw new ChuangkeException(String.format("概算预算信息中【单位:%s,版本：%s】找到多个期间【%s】", bl.get(0).gettEntity(),
					bl.get(0).gettCategory(), times));
		}

		for (BudgetBpcPlan bbp : bl) {
			if (!bbp.gettTime().endsWith("INP")) {
				return bbp.gettTime();
			}
		}
		throw new ChuangkeException(
				String.format("概算预算信息中【单位:%s,版本：%s】未找到合适的期间", bl.get(0).gettEntity(), bl.get(0).gettCategory()));
	}

}
